package com.lingchou.cloud.process.client.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.lc.common.exception.HandlerException;
import com.lingchou.cloud.process.api.dto.param.DynamicTaskParam;
import com.lingchou.cloud.process.api.dto.param.ProcessNode;
import com.lingchou.cloud.process.api.dto.param.SummaryApproveParam;
import com.lingchou.cloud.process.api.enums.CommentTypeEnum;
import com.lingchou.cloud.process.client.cmd.SummaryApprovalCmd;
import com.lingchou.cloud.process.client.dao.DefinitionSupplementaryDao;
import com.lingchou.cloud.process.client.entity.DefinitionSupplementary;
import com.lingchou.cloud.process.client.service.DynamicAsyncService;
import com.lingchou.cloud.process.client.service.FlowAbleCommentService;
import com.lingchou.cloud.process.client.util.ThreadLocalUtil;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.*;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

@Service
@Async
public class DynamicAsyncServiceImpl implements DynamicAsyncService {

    private final ManagementService managementService;
    private final TaskService taskService;
    private final RuntimeService runtimeService;
    private final RepositoryService repositoryService;
    private final DefinitionSupplementaryDao definitionSupplementaryDao;
    private final FlowAbleCommentService flowAbleCommentService;
    private final DynamicBpmnService dynamicBpmnService;

    public DynamicAsyncServiceImpl(ManagementService managementService, TaskService taskService,
                                   RuntimeService runtimeService, RepositoryService repositoryService, DefinitionSupplementaryDao definitionSupplementaryDao, FlowAbleCommentService flowAbleCommentService, DynamicBpmnService dynamicBpmnService) {
        this.managementService = managementService;
        this.taskService = taskService;
        this.runtimeService = runtimeService;
        this.repositoryService = repositoryService;
        this.definitionSupplementaryDao = definitionSupplementaryDao;
        this.flowAbleCommentService = flowAbleCommentService;
        this.dynamicBpmnService = dynamicBpmnService;
    }

    @Override
    public void summaryApprove(SummaryApproveParam summaryApproveParam) {
        summaryApproveParam.getTasks().forEach(t -> dynamicChange(summaryApproveParam.getProcessNodes(), t,
                summaryApproveParam.getOperator(), summaryApproveParam.getComment()));
    }

    private void dynamicChange(List<ProcessNode> processNodes, DynamicTaskParam dynamicTaskParam, String operator, String comment) {
        //查询当前任务
        TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(dynamicTaskParam.getTaskId()).singleResult();
        //获取当前流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(taskEntity.getProcessInstanceId()).singleResult();
        //拿到当前task
        Process process = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()).getMainProcess();
        List<UserTask> userTasks = process.findFlowElementsOfType(UserTask.class);
        UserTask userTask = userTasks.stream().filter(u -> taskEntity.getTaskDefinitionKey().equals(u.getId())).findFirst().orElseThrow(NullPointerException::new);
        List<FlowElement> elements = new ArrayList<>();
        //构建task与连线
        //寻找TimeServiceTask 以当前的方式，必然有
        ServiceTask timeServiceTask = process.findFlowElementsOfType(ServiceTask.class).get(0);
        //查找结束点
        EndEvent endEvent = process.findFlowElementsOfType(EndEvent.class).get(0);
        //启动变量 针对会签节点的实例的人的集合
        Map<String, Object> variables = new HashMap<>(16);
        //配置变量
        List<DefinitionSupplementary> varList = new ArrayList<>();
        //当前节点
        ProcessNode currNode;
        //最后一个节点ID 初始是开始节点的id
        String lastElementId = userTask.getId();
        //遍历生成
        for (ProcessNode processNode : processNodes) {
            //赋值curr为头节点
            currNode = processNode;
            //初始curr的task的id
            String currTaskId = "DC" + IdUtil.simpleUUID();
            //上一节点的出口线
            String lastLineId = "ID" + IdUtil.simpleUUID();
            SequenceFlow sequenceFlow = createSequenceFlow(lastElementId,
                    currTaskId, lastLineId, "N" + lastLineId, StrUtil.EMPTY);
            elements.add(sequenceFlow);
            //人员集合变量名
            String collectionString = "P" + IdUtil.simpleUUID();
            switch (currNode.getNodeType()) {
                case SINGLE:
                    createAssigneeUserTask(elements, currTaskId, currNode.getNodeName(),
                            currNode.getUserIds().get(0), dynamicTaskParam.getEndTime().toString(), timeServiceTask.getId());
                    //塞入循环的变量
                    variables.put(collectionString, currNode.getUserIds().get(0));
                    break;
                case WITH_SIGN:
                    createUserTaskCountersign(elements, currTaskId, currNode.getNodeName(), collectionString,
                            "${nrOfCompletedInstances/nrOfInstances == 1}", dynamicTaskParam.getEndTime().toString(), timeServiceTask.getId());
                    //塞入循环的变量
                    variables.put(collectionString, currNode.getUserIds());
                    break;
                case OR_SIGN:
                    createUserTaskCountersign(elements, currTaskId, currNode.getNodeName(), collectionString,
                            "${nrOfCompletedInstances == 1}", dynamicTaskParam.getEndTime().toString(), timeServiceTask.getId());
                    //塞入循环的变量
                    variables.put(collectionString, currNode.getUserIds());
                    break;
                default:
                    throw new HandlerException("流程定义节点判断异常");
            }
            //赋值最后一个Task
            lastElementId = currTaskId;
            //添加变量
            DefinitionSupplementary definitionSupplementary = new DefinitionSupplementary();
            definitionSupplementary.setId(IdUtil.simpleUUID());
            definitionSupplementary.setTaskDefId(currTaskId);
            definitionSupplementary.setSelectionRange(currNode.getSelectionRange());
            definitionSupplementary.setPersonCollParamName(collectionString);
            definitionSupplementary.setDueTimeParamName(dynamicTaskParam.getEndTime().toString());
            String nodeTypeParamName = "P" + IdUtil.simpleUUID();
            variables.put(nodeTypeParamName, currNode.getNodeType().name());
            definitionSupplementary.setNodeTypeParamName(nodeTypeParamName);
            definitionSupplementary.setCreateTime(LocalDateTime.now());
            definitionSupplementary.setUpdateTime(LocalDateTime.now());
            varList.add(definitionSupplementary);
        }
        //结束线
        elements.add(createSequenceFlow(lastElementId,
                endEvent.getId(), "ID" + IdUtil.simpleUUID(), "endLine", StrUtil.EMPTY));
        //变量存储
        varList.forEach(definitionSupplementary -> definitionSupplementary.setProcDefId(processInstance.getProcessDefinitionId()));
        definitionSupplementaryDao.saveAll(varList);
        //执行
        managementService.executeCommand(new SummaryApprovalCmd(taskEntity.getProcessInstanceId(), userTask, elements));
        //完成本节点
        //修改执行人 其实我这里就相当于签收了
        taskService.setAssignee(dynamicTaskParam.getTaskId(), operator);
        //修改节点完成条件
        dynamicBpmnService.changeMultiInstanceCompletionCondition(taskEntity.getTaskDefinitionKey(),
                "nrOfCompletedInstances > 1");
        //塞进ThreadLocal
        ThreadLocalUtil.putStatus(CommentTypeEnum.APPROVAL);
        //执行任务
        taskService.complete(dynamicTaskParam.getTaskId(), variables);
        //生成审批意见
        flowAbleCommentService.addComment(dynamicTaskParam.getTaskId(), operator,
                taskEntity.getProcessInstanceId(), CommentTypeEnum.APPROVAL.toString(), comment);
    }


    /**
     * 创建连线
     *
     * @param from                起始元素 id
     * @param to                  指向元素 id
     * @param id                  指定 id
     * @param name                指定 name
     * @param conditionExpression 条件表达式
     */
    public SequenceFlow createSequenceFlow(String from, String to, String id, String name, String conditionExpression) {
        SequenceFlow flow = new SequenceFlow();
        flow.setSourceRef(from);
        flow.setTargetRef(to);
        flow.setName(name);
        flow.setId(id);
        if (StrUtil.isNotEmpty(conditionExpression)) {
            flow.setConditionExpression(conditionExpression);
        }
        return flow;
    }

    /**
     * 创建userTask 并锁定人员
     *
     * @param id       指定 id
     * @param name     指定 name
     * @param assignee 指定人员
     * @return userTask对象
     */
    public void createAssigneeUserTask(List<FlowElement> elements, String id, String name, String assignee, String endTime, String timeServiceTaskId) {
        UserTask userTask = createUserTask(id, name);
        userTask.setAssignee(assignee);
        userTask.setOwner(assignee);
        elements.add(userTask);
        //添加定时边界事件
        BoundaryEvent boundaryEvent = addBoundaryEvent(userTask, endTime);
        elements.add(boundaryEvent);
        userTask.setDueDate(endTime);
        SequenceFlow sequenceFlow = createSequenceFlow(boundaryEvent.getId(), timeServiceTaskId, "ID" + IdUtil.simpleUUID(),
                "Name" + IdUtil.simpleUUID(), StrUtil.EMPTY);
        elements.add(sequenceFlow);
    }

    /**
     * 创建userTask
     *
     * @param id   指定 id
     * @param name 指定 name
     * @return userTask 对象
     */
    public UserTask createUserTask(String id, String name) {
        UserTask userTask = new UserTask();
        userTask.setName(name);
        userTask.setId(id);
        return userTask;
    }

    public BoundaryEvent addBoundaryEvent(Activity attachedToRef, String endTime) {
        BoundaryEvent boundaryEvent = new BoundaryEvent();
        boundaryEvent.setId("ID" + IdUtil.simpleUUID());
        boundaryEvent.setAttachedToRef(attachedToRef);
        TimerEventDefinition timerEventDefinition = new TimerEventDefinition();
        timerEventDefinition.setTimeDate(endTime);
        //任务结束
        boundaryEvent.setCancelActivity(true);
        boundaryEvent.setEventDefinitions(Collections.singletonList(timerEventDefinition));
        return boundaryEvent;
    }

    /**
     * 创建会签
     *
     * @param id   taskId
     * @param name taskName
     * @return 一个可并签的userTask
     */
    public UserTask createUserTaskCountersign(List<FlowElement> elements, String id, String name, String collectionString,
                                              String completionCondition, String endTime, String timeServiceTaskId) {
        UserTask userTask = createUserTask(id, name);
        elements.add(userTask);
        userTask.setAssignee("${assignee}");
        userTask.setOwner("${assignee}");
        MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
        multiInstanceLoopCharacteristics.setSequential(false);
        multiInstanceLoopCharacteristics.setInputDataItem("${" + collectionString + "}");
        multiInstanceLoopCharacteristics.setElementVariable("assignee");
        multiInstanceLoopCharacteristics.setCompletionCondition(completionCondition);
        userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
        //添加定时边界事件
        BoundaryEvent boundaryEvent = addBoundaryEvent(userTask, endTime);
        elements.add(boundaryEvent);
        userTask.setDueDate(endTime);
        elements.add(createSequenceFlow(boundaryEvent.getId(), timeServiceTaskId, "ID" + IdUtil.simpleUUID(), "Name" +
                IdUtil.simpleUUID(), StrUtil.EMPTY));
        return userTask;
    }
}
